﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Lucene.Net.Index;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Store;
using Universum.Model.Domain;
using Universum.Infrastructure.Utils;
using Lucene.Net.Documents;
using System.Threading;

namespace Universum.Model.Services.Indexing
{
	/// <summary>
	/// Builds an index using Lucene
	/// </summary>
	public class LuceneIndexBuilder : IDisposable
	{
		/// <summary>
		/// The indexing directory
		/// </summary>
		private Directory _IndexDirectory;
		/// <summary>
		/// The index writer
		/// </summary>
		private IndexWriter _IndexWriter;
		/// <summary>
		/// Returns true if the writer was closed/disposed
		/// </summary>
		public bool IsClosed { get; private set; }
		/// <summary>
		/// Returns true if the Index is being rebuilt
		/// </summary>
		public bool IsRebuildingIndex { get; private set; }
		/// <summary>
		/// Creates a index builder using lucene
		/// </summary>
		/// <param name="directoryPath">The path to the index directory</param>
		/// <param name="rebuildIndex">Specifies if it's going to rebuild the index</param>
		public LuceneIndexBuilder(string directoryPath,
			bool rebuildIndex)
		{
			_IndexDirectory = FSDirectory.GetDirectory(directoryPath,null);
			//_IndexDirectory.SetLockFactory(new NoLockFactory());
			
			IsRebuildingIndex = rebuildIndex;
			InitializeIndexWriter();
		}
		/// <summary>
		/// Creates a index builder using lucene
		/// </summary>
		/// <param name="directory">The directory to use for the index</param>
		/// <param name="rebuildIndex">Specifies if it's going to rebuild the index</param>
		public LuceneIndexBuilder(Directory directory,
			bool rebuildIndex)
		{
			_IndexDirectory = directory;
			IsRebuildingIndex = rebuildIndex;
			InitializeIndexWriter();
		}

		private void InitializeIndexWriter()
		{
			if (!IndexReader.IndexExists(_IndexDirectory)
				|| IsRebuildingIndex)
			{
				_IndexWriter = new IndexWriter(
					_IndexDirectory,
					new StandardAnalyzer(),
					true);
			}
			else
			{
				_IndexWriter = new IndexWriter(
					_IndexDirectory,
					new StandardAnalyzer(),
					false);
			}
			
		}
		#region IDisposable Members
		/// <summary>
		/// Disposes of the writer
		/// </summary>
		public void Dispose()
		{
			Close();
		}
		public void Flush()
		{
			if (!IsClosed && _IndexWriter != null)
			{
				_IndexWriter.Flush(true, true);
				_IndexWriter.Optimize();
			}
		}
		/// <summary>
		/// Closes the writer, and saves all results
		/// </summary>
		private void Close()
		{
			if (!IsClosed && _IndexWriter != null)
			{
				_IndexWriter.Flush(true,true);
				_IndexWriter.Optimize();
				_IndexWriter.Close();
				
				_IndexDirectory.Close();
				IsClosed = true;

			}
		}

		#endregion
		/// <summary>
		/// Adds a new document to the index
		/// </summary>
		/// <param name="document">The document to add</param>
		public void AddDocument(Document document)
		{
			if (_IndexWriter == null)
			{
				InitializeIndexWriter();
			}

			_IndexWriter.AddDocument(document);
			
			
		}
		/// <summary>
		/// Updates the document in the index
		/// </summary>
		/// <param name="document">The document to update</param>
		public void UpdateDocument(Document document)
		{
			if (!IsRebuildingIndex)
			{
				RemoveDocument(document);
				AddDocument(document);
			}
		}
		
		/// <summary>
		/// Removes the document
		/// </summary>
		/// <param name="document">The document marked for removal</param>
		public void RemoveDocument(Document document)
		{
			if (!IsRebuildingIndex)
			{
				try
				{
					_IndexWriter.Close();
					_IndexWriter = null;
					// Here we get the domain's base entity identifier
					Term term = new Term(MemberUtil<DomainEntity>.MemberName(it => it.ID),
						document.Get(MemberUtil<DomainEntity>.MemberName(it => it.ID)));
					IndexReader reader = IndexReader.Open(_IndexDirectory);
					// goes through all documents found

					var termDocs = reader.TermDocs(term);

					while (termDocs.Next())
					{
						reader.DeleteDocument(termDocs.Doc());
					}
					// commits any changes to the index
					reader.Commit();
					// closes the reader
					reader.Close();
				}
				catch 
				{
					// remove failed, either the document was not found
					// or it did not exist in the first place
				}
			}
		}
		/// <summary>
		/// Returns the number of documents in the index
		/// </summary>
		public int Length
		{
			get
			{
				// if we closed the index writer
				if (_IndexWriter != null && !IsClosed)
					return _IndexWriter.DocCount();
				else
				{
					// then we use a reader to get the
					// number of documents
					IndexReader reader = IndexReader.Open(_IndexDirectory);
					return reader.NumDocs();
				}
			}
		}
	}
	
}
